package org.zh.client.jdbc;

import com.alibaba.druid.util.JdbcUtils;
import com.alibaba.druid.util.ResultSetConsumer;
import org.zh.client.config.SpringContextUtil;

import javax.sql.DataSource;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.sql.*;
import java.util.*;

import static org.zh.client.config.GlobalStatic.connectionThreadLocal;


public class ZHOpenJdbcUtil {

    private static final DataSource dataSource = SpringContextUtil.getBean(DataSource.class);

    private static Connection getConn() {
        Connection connection = connectionThreadLocal.get();
        if (connection == null) {
            try {
                connection = dataSource.getConnection();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
        return connection;
    }


    public static int executeUpdate(String sql, List<Object> parameters) throws SQLException {
        return JdbcUtils.executeUpdate(getConn(), sql, parameters);
    }

    public static int executeUpdate(String sql, Object... parameters) throws SQLException {
        return JdbcUtils.executeUpdate(getConn(), sql, Arrays.asList(parameters));
    }

    public static void execute(String sql) throws SQLException {
        JdbcUtils.execute(getConn(), sql, Collections.emptyList());
    }

    public static void execute(String sql, List<Object> parameters) throws SQLException {
        JdbcUtils.execute(getConn(), sql, parameters);
    }

    public static void execute(String sql, Object... parameters) throws SQLException {
        JdbcUtils.execute(getConn(), sql, Arrays.asList(parameters));
    }

    public static List<Map<String, Object>> executeQuery(String sql, List<Object> parameters)
            throws SQLException {
        return JdbcUtils.executeQuery(getConn(), sql, parameters);
    }

    public static List<Map<String, Object>> executeQuery(String sql, Object... parameters)
            throws SQLException {
        return JdbcUtils.executeQuery(getConn(), sql, Arrays.asList(parameters));
    }

    public static void insertToTable(String tableName, Map<String, Object> data) throws SQLException {
        JdbcUtils.insertToTable(getConn(), tableName, data);
    }

    private static <T> T insertToTable(T t) throws Throwable {
        Connection conn = getConn();
        Class<?> aClass = t.getClass();
        String tableName = TCCache.getTableName(aClass);
        Map<String, Object> data = TCCache.beanToMapRef(t);
        String sql = JdbcUtils.makeInsertToTableSql(tableName, data.keySet());
        List<Object> parameters = new ArrayList<>(data.values());
        PreparedStatement ps = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
        for (int i = 0, size = parameters.size(); i < size; ++i) {
            Object param = parameters.get(i);
            ps.setObject(i + 1, param);
        }
        int i = ps.executeUpdate();
        String idName = TCCache.getClassId(tableName);
        if (i > 0 && idName != null) {
            ResultSet gk = ps.getGeneratedKeys();
            MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(aClass, MethodHandles.lookup());
            MethodHandle setter = lookup.findSetter(aClass, idName, aClass.getDeclaredField(idName).getType());
            if (gk.next()) {
                setter.invoke(t, gk.getLong(1));
            }
            gk.close();
        }
        ps.close();
        conn.close();
        return t;
    }

    public static <T> T executeQuery(ResultSetConsumer<T> consumer, String sql, Object... parameters) throws SQLException {
        return executeQuery(consumer, sql, Arrays.asList(parameters));
    }

    public static <T> T executeQuery(ResultSetConsumer<T> consumer, String sql, List<Object> parameters) throws SQLException {
        PreparedStatement stmt = null;
        ResultSet rs = null;
        Connection conn = null;
        T object = null;
        try {
            conn = getConn();
            stmt = conn.prepareStatement(sql);
            for (int i = 0; i < parameters.size(); ++i) {
                stmt.setObject(i + 1, parameters.get(i));
            }
            rs = stmt.executeQuery();
            while (rs.next()) {
                if (consumer != null) {
                    object = consumer.apply(rs);
                    consumer.accept(object);
                }
            }
            return object;
        } finally {
            JdbcUtils.close(rs);
            JdbcUtils.close(stmt);
            JdbcUtils.close(conn);
        }
    }

    public static <T> List<T> executeQueryList(ResultSetConsumer<T> consumer, String sql, Object... parameters) throws SQLException {
        return executeQueryList(consumer, sql, Arrays.asList(parameters));
    }

    public static <T> List<T> executeQueryList(ResultSetConsumer<T> consumer, String sql, List<Object> parameters) throws SQLException {
        PreparedStatement stmt = null;
        ResultSet rs = null;
        Connection conn = null;
        List<T> list = new ArrayList<>();
        try {
            conn = getConn();
            stmt = conn.prepareStatement(sql);
            for (int i = 0; i < parameters.size(); ++i) {
                stmt.setObject(i + 1, parameters.get(i));
            }
            rs = stmt.executeQuery();
            while (rs.next()) {
                if (consumer != null) {
                    T object = consumer.apply(rs);
                    list.add(object);
                    consumer.accept(object);
                }
            }
            return list;
        } finally {
            JdbcUtils.close(rs);
            JdbcUtils.close(stmt);
            JdbcUtils.close(conn);
        }
    }


}